﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TexasHoldem.RankedHands
{
    public abstract class RankedHand: Hand, IComparable<RankedHand>
    {
        protected RankedHand(Hand hand) : base(hand.Cards) { }

        public abstract string RankName { get; }
        protected abstract ushort RankingStrength { get; }
        protected abstract IEnumerable<Card> FindDefiningCards();

        public IEnumerable<Card> OrderedCards
        {
            get
            {
                var definingCards = FindDefiningCards();
                var kickers = Cards.Except(definingCards).OrderByDescending(c => c.Value);
                return definingCards.Union(kickers);
            }
        }

        public int CompareTo(RankedHand other)
        {
            return ResolveInOrder(
                () => FoldedVsUnfolded(other),
                () => HandRankingStrength(other),
                () => TieBreakingStrategy(other));
        }

        private int ResolveInOrder(params Func<int>[] comparisons)
        {
            foreach (var comparison in comparisons) {
                var comparisonResult = comparison();
                if (comparisonResult != 0)
                    return comparisonResult;
            }

            return 0;
        }

        int FoldedVsUnfolded(RankedHand other)
        {
            return this.Cards.Count().CompareTo(other.Cards.Count());
        }

        int HandRankingStrength(RankedHand other)
        {
            return this.RankingStrength.CompareTo(other.RankingStrength);
        }

        int TieBreakingStrategy(RankedHand other)
        {
            if (this.GetType() != other.GetType())
                throw new InvalidOperationException("Tried to resolve a tie between different hand rankings.");

            return OrderedCards.Zip(other.OrderedCards, (my, theirs) => my.Value.CompareTo(theirs.Value))
                .Take(Math.Min(5, OrderedCards.Count()))
                .FirstOrDefault(result => result != 0);
        }
    }
}
